#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>


enum EXITCODE
{
    DEVNUM_FAILED = -1,
    ADD_FAILED    = -2
};

struct  device_test {
    //设备号
    dev_t dev_num;
    //主设备号
    unsigned major;
    //次设备号
    unsigned minor;
    //cdev结构体
    struct cdev cdev_m;
    //class
    struct class* class;
    //device 
    struct device* device;
    //缓冲区
    char kbuf[1024];
};

struct device_test dev;


static int chrdev_open(struct inode* inode, struct file* file)
{
    printk("chardev is opened\n");
    //打印调用堆栈
    dump_stack();
    //文件私有数据指向设备结构体
    file->private_data = &dev;
    return 0;
}

static ssize_t chrdev_read(struct file* file, \
char __user* buf, size_t size, loff_t *off)
{
    struct device_test* pdev = (struct device_test* ) file->private_data;
    if (size > sizeof(pdev->kbuf))
        size = sizeof(pdev->kbuf);
    if (copy_to_user(buf, pdev->kbuf, size) != 0)
    {
        printk("copy_to_user error\n");

    }
    printk("copy_to_user sucess\n");

    return 0;
}

static ssize_t chrdev_write(struct file* file, \
const char __user* buf, size_t size, loff_t* off)
{
    struct device_test *pdev = (struct device_test*) file->private_data;
    if (copy_from_user(pdev->kbuf, buf, size) != 0)
    {
        printk("copy_from_user failed\n");
        return -1;
    }
    printk("write sucess : %s\n", pdev->kbuf);

    return 0;
}

static int chrdev_release(struct inode* inode, struct file* file)
{
    printk("chardev is released\n");
    return 0;
}


//定义方法集合
static struct file_operations cdev_op = {
    //将owner指向本模块，可以避免
    //模块正在被使用的时候卸载
    .owner   = THIS_MODULE,
    .open    = chrdev_open,
    .read    = chrdev_read,
    .write   = chrdev_write,
    .release = chrdev_release
};


// 驱动入口函数
static int __init chrdev_init(void)
{
    //返回值便于判断
    int ret = 0;
   // major 主设备号, minor 次设备号
    unsigned int major = 0;
    unsigned int minor = 0;

    //自动获取设备号接口alloc_chrdev_region
    ret = alloc_chrdev_region(&dev.dev_num, 0, 1, "chardev_name");
    if (ret < 0)
    {
        printk("get dev_num failed\n");
        return DEVNUM_FAILED;
    }
    printk("get dev_num sucess \n");
    major = MAJOR(dev.dev_num);
    minor = MINOR(dev.dev_num);
    printk("major is %d", major);
    printk("minor is %d", minor);

    // 初始化cdev_m结构体,绑定方法集合
    cdev_init(&dev.cdev_m, &cdev_op);
    printk("cdev_init sucess \n");
    //将owner指向本模块避免模块在使用时被卸载
    dev.cdev_m.owner = THIS_MODULE;



    //绑定设备号到cdev_m结构体
    ret = cdev_add(&dev.cdev_m, dev.dev_num, 1);
    if (ret < 0)
    {
        printk("cdev_add failed\n");
        return ADD_FAILED;
    }    
    printk("cdev_add sucessed \n");

    //初始化类
    dev.class = class_create(THIS_MODULE, "class_test");
    //device_creat 进行设备创建
    device_create(dev.class, NULL, dev.dev_num, NULL, "device_test");
    printk("moudle init sucess\n");
    return 0;
}

// 驱动出口函数
static void __exit chardev_exit(void)
{
    device_destroy(dev.class, dev.dev_num);
    class_destroy(dev.class);
    cdev_del(&dev.cdev_m);
    unregister_chrdev_region(dev.dev_num, 1);
    printk("moudle eixt\n");
}


//注册入口函数
module_init(chrdev_init);
//注册出口函数
module_exit(chardev_exit);
//开源协议版本
MODULE_LICENSE("GPL v2");
